
// 装饰器就是一个函数,只能在类中使用(类本身, 类成员使用)
// 装饰器的分类: 类的装饰器, 方法装饰器, 属性装饰器, 访问装饰器, 参数装饰器

// 1) 类的装饰器 给类来进行扩展, 也可以返回一个子类重写父类(一般不会通过装饰器取扩展类的属性和方法，因为扩展后原来没有的方法无法访问到，需要通过namespace，interface来进行扩展)

const classDecorator = <T extends new (...args:any[])=>void>(target:T) => {
    (target as any).type = "动物";
    (target as any).getType = function(){
      return this.type
    }
    Object.assign(target.prototype,{
      eat(){},
      drink(){}
    })
  }
  function OverrideAnimal(target:any) {
     return class extends target {
       eat(){
        super.eat();
        console.log("new eat");
       }
     }
  }
  // interface Animal {
  //   drink(): void;
  //   eat(): void;
  // }

  function Enum(isEnum: boolean){
   return function(target:any,propertyKey:string,descriptor:any){
    //  descriptor.enummubale // 是否可枚举
    //  descriptor.writable // 是否能被重写
    //  descriptor.configurable // 是否属性能被删除
    //  descriptor.value // 当前函数的值
    descriptor.enumerable = isEnum;
    let original = descriptor.value;
    descriptor.value = function() {
      console.log("prev eat");
      original.call(this, ...arguments);
    }
   }
  }
  
  function ToUpper(isUpper:boolean):PropertyDecorator {
    return function(target, propertyKey) {
      // 不同的es版本会有不同的解析
      console.log(target, propertyKey);
      let val = "";
      Object.defineProperty(target, propertyKey, {
        enumerable: true,
        get() {
          return val.toUpperCase()
        },
        set(newValue){
          val = newValue
        }
      })
  
    }
  }
  // 除了描述类和静态方法之外target都是原型
  
  // 3）可以描述属性访问器 get 和 set
  function valToUpper(target:any, key: string, descriptor:any) {
    console.log(descriptor);
    let originalSet = descriptor.set;
    descriptor.set = function(newValue: string) {
      return originalSet.call(this, newValue.toUpperCase());
    }
  }
  // @classDecorator
  // @OverrideAnimal
  class Animal {
    @ToUpper(true)
    public name:string = "animal"
    @Enum(true)
    eat(){
      console.log("动物 original");
    }
  
  
    private _val!:string;
    @valToUpper
    get val() {
      return this._val;
    }
    set val(newValue:string) {
      this._val = newValue;
    } 
  }
  // console.log((Animal as any).getType());
  const animal = new Animal();
  // animal.eat();
  console.log(animal);
  animal.val = "abc";
  console.log(animal.val);
  // 类的装饰器, 属性装饰器（静态的属性装饰器）,方法装饰器（静态方法装饰器）
  
  export {}